import json
import os
import re
from datetime import datetime
import shutil

from src.utils.logger import log_info


class FileProcessor:
    def __init__(self, directory, status_callback=None):
        """
        初始化 FileProcessor 类
        :param directory: 要扫描的目录路径
        :param status_callback: 状态更新回调函数
        """
        if not os.path.isdir(directory):
            raise ValueError(f"指定的路径不是有效的目录: {directory}")
        self.directory = directory
        self.status_callback = status_callback

        # 创建和加载已处理文件记录
        self.processed_files_db = self.load_processed_files_db()

    def load_processed_files_db(self) -> list:
        """加载已处理文件的数据库"""
        db_path = os.path.join(self.directory, ".processed_files.json")
        if os.path.exists(db_path):
            try:
                with open(db_path, "r", encoding="utf-8") as f:
                    processed_files = json.load(f)
                    self.update_status(f"已加载处理记录，共 {len(processed_files)} 个文件")
                    return processed_files
            except Exception as e:
                self.update_status(f"加载处理记录失败: {e}")
                return []
        return []

    def save_processed_files_db_category(self, category=None):
        json_file = f"{category}/.processed_files.json" if category else ".processed_files.json"
        
        with open(os.path.join(self.directory, json_file), "w", encoding="utf-8") as f:
            filtered_data = [
                record for record in self.processed_files_db
                if record.get("category") == category
            ]
            json.dump(filtered_data, f, ensure_ascii=False, indent=2)

    def save_processed_files_db(self):
        """保存已处理文件的数据库"""
        base_db_path = os.path.join(self.directory, ".processed_files.json")
        
        with open(base_db_path, "w", encoding="utf-8") as f:
            json.dump(self.processed_files_db, f, ensure_ascii=False, indent=2)

    def mark_file_as_processed(self, filename=None, translated_name=None, category=None, advertisement=None
                               ,watermark=None
                               ):
        def is_valid(value):
            return value is not None

        # 查找是否已有记录
        matched = None
        for record in self.processed_files_db:
            if (record.get("filename") == filename) or (record.get("translated_name") == filename):
                matched = record
                break

        # 如果找到记录则更新字段
        if matched:
            if is_valid(translated_name):
                matched["translated_name"] = translated_name
            if is_valid(category):
                matched["category"] = category
            if is_valid(advertisement):
                matched["advertisement"] = advertisement
            if is_valid(watermark):
                matched["watermark"] = watermark
            matched["timestamp"] = datetime.now().isoformat()
        else:
            # 没有找到则创建新记录
            self.processed_files_db.append({
                "filename": filename,
                "timestamp": datetime.now().isoformat(),
                "translated_name": translated_name,
                "category": category,
                "advertisement": advertisement,
                "watermark": watermark
            })
        self.save_processed_files_db()
        
    def is_file_processed(self, filename, attribute=None):
        """
        检查文件是否已处理
        :param filename: 文件名
        :param operation_type: 操作类型（translate/classify/both），如果为None则检查任何操作
        :return: 是否已处理
        """
        fileInfo = next((file for file in self.processed_files_db if file.get("filename") == filename \
            or file.get("translated_name") == filename
            ), None)
        if fileInfo == None:
            return False
        if (attribute):
            if fileInfo[attribute] == None:
                return False
        return True

    def sanitize_filename(self, filename):
        """
        处理文件名，使其在Windows中有效
        :param filename: 原始文件名
        :return: 安全的文件名
        """
        # Windows不允许的字符 \ / : * ? " < > |
        invalid_chars = r'[\\/:*?"<>|]'
        safe_name = re.sub(invalid_chars, '_', filename)

        # 替换控制字符
        safe_name = re.sub(r'[\x00-\x1f]', '', safe_name)

        # # 如果配置为使用ASCII，则替换所有非ASCII字符
        # if self.status_callback and hasattr(app, 'language_var') and app.language_var.get() == 'ASCII':
        #     safe_name = ''.join(c if c.isascii() and c.isprintable() else '_' for c in safe_name)

        # 确保文件名不为空
        if not safe_name or safe_name.isspace():
            safe_name = "file_" + datetime.now().strftime("%Y%m%d%H%M%S")

        # 限制文件名长度（Windows路径最大限制为260个字符，但我们留一些余量给路径）
        if len(safe_name) > 200:
            name_part, ext_part = os.path.splitext(safe_name)
            safe_name = name_part[:200 - len(ext_part)] + ext_part

        # 避免常见的Windows系统保留名称
        reserved_names = ['CON', 'PRN', 'AUX', 'NUL', 'COM1', 'COM2', 'COM3',
                          'COM4', 'COM5', 'COM6', 'COM7', 'COM8', 'COM9',
                          'LPT1', 'LPT2', 'LPT3', 'LPT4', 'LPT5', 'LPT6',
                          'LPT7', 'LPT8', 'LPT9']

        name_without_ext = os.path.splitext(safe_name)[0].upper()
        if name_without_ext in reserved_names:
            safe_name = "file_" + safe_name

        # 移除开头和结尾的空格与点号
        safe_name = safe_name.strip('. ')

        # 最后检查是否为空，如果是空则生成一个随机名称
        if not safe_name:
            safe_name = "file_" + datetime.now().strftime("%Y%m%d%H%M%S")

        return safe_name

    def update_status(self, message):
        """更新状态到UI"""
        if self.status_callback:
            self.status_callback(message)

    def get_video_files_with_types(self, chunk_size=50):
        """
        获取目录下的视频文件及其类型，并按每 chunk_size 个分割一次
        :param chunk_size: 每个分块的最大文件数
        :param skip_processed: 是否跳过已处理的文件
        :param operation_type: 
        :return: 数组，每个元素是一个包含最多 chunk_size 个视频文件及其类型的字典
        """
        self.update_status(f"正在扫描目录: {self.directory}")

        video_files_with_types = {}
        existing_categories = []  # 存储已有分类的文件夹名称
        for file in os.listdir(self.directory):
            if os.path.isfile(os.path.join(self.directory, file)) and file.lower().endswith(
                    ('.mp4', '.avi', '.mkv', '.mov')) and \
                not self.is_file_processed(filename=file, attribute="category"):

                video_name, _ = os.path.splitext(file)
                types = [f"#{part.strip()}" for part in video_name.split("#") if part.strip()]
                video_files_with_types[file] = types
                self.update_status(f"找到视频文件: {file}")
            elif os.path.isdir(os.path.join(self.directory, file)) and file != "_固定水印或字幕":
                existing_categories.append(file)
                self.update_status(f"找到现有分类文件夹: {file}")


        video_files_with_types["已有分类"] = existing_categories

        # 分割为每chunk_size个一组
        video_files_chunks = []
        video_files_list = list(video_files_with_types.items())
        for i in range(0, len(video_files_list), chunk_size):
            chunk = dict(video_files_list[i:i + chunk_size])
            video_files_chunks.append(chunk)

        file_count = len(video_files_list) - 1 if "已有分类" in video_files_with_types else len(video_files_list)
        self.update_status(f"共找到 {file_count} 个待处理视频文件，分为 {len(video_files_chunks)} 组")
        return video_files_chunks

    def organize_videos_by_category(self, video_categories):
        """
        根据视频分类创建文件夹，并将视频移动到对应的分类文件夹下
        如果检测到隐藏文件 .skip_classification 则跳过分类
        :param video_categories: 包含视频分类和视频名称的字典 {视频分类: [视频名称1, 视频名称2]}
        :param target_language: 目标语言代码
        """
        directory = self.directory
        
        self.update_status(f"开始组织视频文件...")
        for category, video_files in video_categories.items():
            # 确保分类文件夹名称也是安全的
            safe_category = self.sanitize_filename(category)
            if safe_category != category:
                self.update_status(f"已修正分类文件夹名称: {category} → {safe_category}")
            
            category_folder = os.path.join(directory, safe_category)
            if not os.path.exists(category_folder):
                os.makedirs(category_folder)
                self.update_status(f"创建分类文件夹: {safe_category}")

            for video_file in video_files:
                source_path = os.path.join(directory, video_file)
                if os.path.exists(source_path):
                    # 检查目标文件是否已存在，如果存在则添加时间戳
                    target_path = os.path.join(category_folder, video_file)
                    if os.path.exists(target_path):
                        base_name, ext = os.path.splitext(video_file)
                        time_stamp = datetime.now().strftime("_%Y%m%d%H%M%S")
                        safe_file_name = f"{base_name}{time_stamp}{ext}"
                        target_path = os.path.join(category_folder, safe_file_name)
                        self.update_status(f"目标文件已存在，添加时间戳: {safe_file_name}")
                    # 尝试移动文件
                    try:
                        shutil.move(source_path, target_path)
                        self.update_status(
                            f"视频文件已分类: {source_path} → {target_path}")
                        self.mark_file_as_processed(filename=video_file,
                                                    category=safe_category)
                    except (PermissionError, OSError) as e:
                        # 如果移动失败，尝试复制然后删除
                        self.update_status(f"移动文件失败，尝试复制: {e}")
                        shutil.copy2(source_path, target_path)
                        os.remove(source_path)
                        self.update_status(
                            f"视频文件已通过复制方式分类: {source_path} -> {target_path}")
                    except Exception as final_error:
                        self.update_status(f"所有尝试均失败，跳过此文件: {final_error}")
                else:
                    self.update_status(f"文件未找到: {source_path}")
            self.save_processed_files_db_category(category=safe_category)
 
    def skip_classification(self):
        """
        检查目录中是否存在视频文件，如果没有视频文件则返回 True
        :return: 如果没有视频文件返回 True，否则返回 False
        """
        for file in os.listdir(self.directory):
            if os.path.isfile(os.path.join(self.directory, file)) and file.lower().endswith(
                    ('.mp4', '.avi', '.mkv', '.mov')):
                self.update_status("检测到视频文件: 开始分类")
                return False
        self.update_status("没有检测到视频文件，跳过分类")
        return True
